package com.lhczf;

import com.lhczf.analyzer.lucene.IKAnalyzer;
import com.lhczf.config.SystemConfig;
import jdk.nashorn.internal.runtime.arrays.ArrayLikeIterator;
import org.apache.log4j.BasicConfigurator;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.MMapDirectory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.CharBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;

/**
 * @author carl
 */
public class App {

    private static final Logger logger = LoggerFactory.getLogger(App.class);
    private static String indexDirectory = null;
    private static String docsPath = "E:\\lucene\\data";

    public static void main(String[] args) {
        BasicConfigurator.configure();
        SystemConfig systemConfig = new SystemConfig();
        systemConfig.loadSystemConfig();

        indexDirectory = systemConfig.getTheRootDirOfIndex();

        File indexFile = new File(indexDirectory);
        Directory directory = null;
        boolean create = true;
        try {
            //指定索引库的存放位置Directory对象
            directory = MMapDirectory.open(indexFile.toPath());
        } catch (IOException e) {
            logger.error("", e);
        }
        //指定一个分析器，对文档内容进行分析
        Analyzer analyzer = new IKAnalyzer();
        //Analyzer analyzer = new StandardAnalyzer();
        //createIndexs(directory, create, analyzer);
        try {
            searchIndexs(directory, analyzer);
        } catch (Exception e) {
            logger.error("", e);
        }
    }

    private static void searchIndexs(Directory directory, Analyzer analyzer) throws IOException, ParseException {

        IndexReader reader = DirectoryReader.open(directory);
        IndexSearcher searcher = new IndexSearcher(reader);

        QueryParser parser = new QueryParser("operSentence", analyzer);
        Query query = parser.parse("FULL");

        //query = IntPoint.newExactQuery("age", 30);

        Sort sort = new Sort(new SortField[]{new SortField("sqlExecTime", SortField.Type.FLOAT, true)});

        TopDocs results = searcher.search(query, 20, sort);
        ScoreDoc[] hits = results.scoreDocs;

        int numTotalHits = Math.toIntExact(results.totalHits);
        int start = 0;
        int end = Math.min(numTotalHits, 20);

        for (int i = start; i < end; i++) {
            Document doc = searcher.doc(hits[i].doc);
            System.out.println("the sqlExecTime is: " + doc.get("sqlExecTime") + ", dealState is: " + doc.get("dealState") + ", the operSentence is: " + doc.get("operSentence"));
        }
    }

    private static void createIndexs(Directory directory, boolean create, Analyzer analyzer) {
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        System.out.println("config.getRAMBufferSizeMB(): " + config.getRAMBufferSizeMB());
        System.out.println("config.getMaxBufferedDocs(): " + config.getMaxBufferedDocs());
        config.setRAMBufferSizeMB(100);

        if (create) {
            config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
        } else {
            config.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
        }

        IndexWriter indexWriter = null;
        try {
            indexWriter = new IndexWriter(directory, config);
            indexWriter.forceMerge(20);

        } catch (IOException e) {
            logger.error("", e);
        }
        final Path docDir = Paths.get(docsPath);
        if (!Files.isReadable(docDir)) {
            System.out.println("Document directory '" + docDir.toAbsolutePath() + "' does not exist or is not readable, please check the path");
            System.exit(1);
        }
        try {
            indexDocs(indexWriter, docDir);
            indexWriter.close();
        } catch (IOException e) {
            logger.error("", e);
        }
    }

    private static void indexDocs(final IndexWriter writer, Path path) throws IOException {
        boolean isDir = Files.isDirectory(path);
        if (isDir) {
            SimpleFileVisitor simpleFileVisitor = new SimpleFileVisitor<Path>() {
                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                    indexDoc(writer, file, attrs.lastModifiedTime().toMillis());
                    return FileVisitResult.CONTINUE;
                }
            };
            try {
                Files.walkFileTree(path, simpleFileVisitor);
            } catch (IOException e) {
                logger.error("", e);
            }
        } else {
            indexDoc(writer, path, Files.getLastModifiedTime(path).toMillis());
        }
    }

    private static void indexDoc(IndexWriter writer, Path file, long lastModified) throws IOException {
        Date start = new Date();

        int total = 0;
//        RandomAccessFile raf = new RandomAccessFile(file.toString(), "rw");
//        FileChannel fc = raf.getChannel();
//        MappedByteBuffer mbuff = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
//        Charset charset = Charset.forName("utf-8");
//        CharsetDecoder decoder = charset.newDecoder();
//        CharBuffer charBuffer = decoder.decode(mbuff);
//        String lineSeparator = System.getProperty("line.separator");
//        Scanner sc = new Scanner(charBuffer).useDelimiter(lineSeparator);
//        while (sc.hasNext()) {
//            String content = sc.nextLine();
//            dealRecordInFile(writer, content);
//            total++;
//        }
//        fc.close();

        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(file.toString())));
        BufferedReader in = new BufferedReader(new InputStreamReader(bis, "utf-8"), 1 * 1024 * 1024);

        while (in.ready()) {
            String content = in.readLine();
            Document doc = dealRecordInFile(writer, content);
            writer.addDocument(doc);
            total++;
        }
        System.out.println("&&&&&&&&&&&&&&&&&&&&=" + total);
        Date end = new Date();
        long times = end.getTime() - start.getTime();
        System.out.println(times + " total milliseconds" + "$$$$$$$$$$=" + total / (times / 1000));
    }

    private static Document dealRecordInFile(IndexWriter writer, String content) throws IOException {
        Document doc = new Document();
        String[] fields = content.split("```");
        int count = fields.length;
        for (int i = 0; i < count; i = i + 2) {
            String name = fields[i];
            if ((i+1) >= count) {
                break;
            }
            String value = fields[i + 1];
            Field field;
            switch (name) {
                case "time":
                case "srcIp":
                case "srcPort":
                case "destIp":
                case "destPort":
                case "sentenceLen":
                case "rowNum":
                case "dealState":
                case "riskLev":
                    Long date = Long.parseLong(value);
                    field = new LongPoint(name, date);
                    saveNumberType(doc, name, date);
                    break;
                case "sqlExecTime":
                    Float aFloat = Float.valueOf(value);
                    field = new FloatPoint(name, aFloat);
                    saveNumberType(doc, name, aFloat);
                    break;
                case "operSentence":
                case "returnedContent":
                    field = new TextField(name, value, Field.Store.YES);
                    break;
                default:
                    field = new StringField(name, value, Field.Store.YES);
                    break;
            }
            doc.add(field);
        }
        return doc;
    }

    private static void saveNumberType(Document doc, String name, Object date) {
        if (date instanceof Long) {
            doc.add(new NumericDocValuesField(name, (long) date));
            doc.add(new StoredField(name, (long) date));
        } else if (date instanceof Float) {
            doc.add(new FloatDocValuesField(name, (float) date));
            doc.add(new StoredField(name, (float) date));
        }

    }
}
